﻿using Common.EF.Structs;
using Common.Infrastructure;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;

namespace Common.EF.Utils
{
    public class ContextInitUtils
    {
        public static List<CommonContextParam> GetContextParamConfig(IConfiguration configuration)
        {
            return DoInitAccessConfig(configuration);
        }

        private static XmlElement GetElementByTagName(XmlElement ele, string name)
        {
            XmlElement ret = null;
            var accessTypes = ele.GetElementsByTagName(name);
            foreach (var a in accessTypes)
            {
                ret = (XmlElement)a;
                break;
            }

            return ret;
        }

        private static List<CommonContextParam> DoInitAccessConfig(IConfiguration configuration)
        {
            List<CommonContextParam> ret = new List<CommonContextParam>();
            var files = System.IO.Directory.GetFiles(System.AppContext.BaseDirectory, "ContextConfig*.xml");
            var pfiles = GetLibContextAccessParamsByFiles(files);
            if (pfiles != null && pfiles.Any())
            {
                ret.AddRange(pfiles);
            }

            var timeRets = GetLibContextParamsByTime(configuration);
            if (timeRets != null && timeRets.Any())
            {
                ret.AddRange(timeRets);
            }

            return ret;
        }

        private static List<CommonContextParam> GetLibContextAccessParamsByFiles(string[] files)
        {
            List<CommonContextParam> ret = new List<CommonContextParam>();
            if (files == null || !files.Any())
            {
                return ret;
            }

            foreach (var file in files)
            {
                XmlDocument xx = new XmlDocument();
                xx.Load(file);
                var x1 = xx.DocumentElement;
                var xs = x1.GetElementsByTagName("context");
                foreach (var x in xs)
                {
                    var node = (XmlElement)x;
                    var name = node.GetAttribute("name");
                    if (string.IsNullOrEmpty(name))
                    {
                        continue;
                    }

                    CommonContextParam contextParam = new CommonContextParam
                    {
                        Code = name
                    };
                    contextParam.Code = contextParam.Code.PadLeft(8, '0');
                    contextParam.DBType = (DataBaseTypeEnum)LibSysUtils.ToInt(node.GetAttribute("dbType"));
                    contextParam.ContextType = (ContextTypeEnum)LibSysUtils.ToInt(node.GetAttribute("contextType"));
                    var connNode = GetElementByTagName(node, "conn");
                    if (connNode != null && !string.IsNullOrEmpty(connNode.InnerText))
                    {
                        contextParam.ConnString = connNode.InnerText;
                    }

                    var timeNode = GetElementByTagName(node, "timeInterval");
                    if (timeNode != null)
                    {
                        var timeInterval = GetElementByTagName(timeNode, "timeInterval");
                        var readTimeInterval = GetElementByTagName(timeNode, "readTimeInterval");
                        if (timeInterval != null)
                        {
                            contextParam.BeginTime = LibSysUtils.ToDateTime(timeInterval.GetAttribute("begin"));
                            contextParam.EndTime = LibSysUtils.ToDateTime(timeInterval.GetAttribute("end"));
                        }

                        if (readTimeInterval != null)
                        {
                            contextParam.ReadBeginTime = LibSysUtils.ToDateTime(readTimeInterval.GetAttribute("begin"));
                            contextParam.ReadEndTime = LibSysUtils.ToDateTime(readTimeInterval.GetAttribute("end"));
                        }
                    }

                    ret.Add(contextParam);
                }
            }

            return ret;
        }

        private static List<CommonContextParam> GetLibContextParamsByTime(IConfiguration configuration)
        {
            List<CommonContextParam> ret = new List<CommonContextParam>();
            var files = System.IO.Directory.GetFiles(System.AppContext.BaseDirectory, "ContextConfig*.xml");
            if (files == null || !files.Any())
            {
                return ret;
            }

            List<ContextTimeParam> timeParams = new List<ContextTimeParam>();
            foreach (var file in files)
            {
                XmlDocument xx = new XmlDocument();
                xx.Load(file);
                var x1 = xx.DocumentElement;
                var xs = x1.GetElementsByTagName("contextTime");
                foreach (var x in xs)
                {
                    var node = (XmlElement)x;
                    ContextTimeParam timeParam = new ContextTimeParam
                    {
                        DbType = (DataBaseTypeEnum)LibSysUtils.ToInt(node.GetAttribute("dbType"))
                    };

                    var timeTypeNode = GetElementByTagName(node, "timeType");
                    if (timeTypeNode != null)
                    {
                        timeParam.TimeType = (ContextTimeContextTimeTypeEnum)LibSysUtils.ToInt(timeTypeNode.GetAttribute("contextType"));
                        if (!string.IsNullOrEmpty(timeTypeNode.InnerText))
                        {
                            timeParam.Time = LibSysUtils.ToInt(timeTypeNode.InnerText);
                        }

                        if (timeParam.Time == 0)
                        {
                            timeParam.Time = 1;
                        }
                    }

                    var timeNode = GetElementByTagName(node, "timeInterval");
                    if (timeNode != null)
                    {
                        timeParam.BeginTime = LibSysUtils.ToDateTime(timeNode.GetAttribute("begin"));
                        timeParam.EndTime = LibSysUtils.ToDateTime(timeNode.GetAttribute("end"));
                        if (timeParam.BeginTime == null)
                        {
                            timeParam.BeginTime = new DateTime(2019, 12, 1);
                        }

                        if (timeParam.EndTime == null)
                        {
                            timeParam.EndTime = DateTime.MaxValue;
                        }
                    }

                    var hostsNode = GetElementByTagName(node, "hosts");
                    if (hostsNode != null)
                    {
                        var hosts = hostsNode.GetElementsByTagName("host");
                        foreach (var h in hosts)
                        {
                            var hnode = (XmlElement)h;
                            var host = hnode.GetAttribute("host");
                            if (string.IsNullOrEmpty(host))
                            {
                                continue;
                            }

                            var hp = new ContextTimeHostParam
                            {
                                Host = host,
                                Post = hnode.GetAttribute("post"),
                                Password = hnode.GetAttribute("password"),
                                CustomParam = hnode.GetAttribute("param"),
                                ContextIndex = LibSysUtils.ToInt(hnode.GetAttribute("contextIndex")),
                                ContextCount = LibSysUtils.ToInt(hnode.GetAttribute("contextCount"))
                            };
                            if (hp.ContextCount == 0)
                            {
                                hp.ContextCount = 1;
                            }

                            timeParam.Hosts.Add(hp);
                        }
                    }

                    timeParams.Add(timeParam);
                }
            }

            var moduleCode = configuration != null ? configuration["ServiceCode"] : string.Empty;
            timeParams = timeParams.OrderBy(t => t.BeginTime).ThenBy(t => t.EndTime).ToList();
            ContextTimeParam lastParam = null;
            foreach (var item in timeParams)
            {
                if (item.TimeType == ContextTimeContextTimeTypeEnum.Day)
                {
                    item.BeginTime = item.BeginTime.Value.Date;
                    if (item.EndTime.HasValue && item.EndTime != DateTime.MaxValue)
                    {
                        item.EndTime = item.EndTime.Value.Date.AddDays(1).AddSeconds(-1);
                    }
                }
                else if (item.TimeType == ContextTimeContextTimeTypeEnum.Month)
                {
                    DateTime d = item.BeginTime.Value.Date;
                    item.BeginTime = d.AddDays(1 - d.Day);
                    if (item.EndTime.HasValue && item.EndTime != DateTime.MaxValue)
                    {
                        DateTime d1 = item.EndTime.Value.Date;
                        item.EndTime = d1.AddDays(1 - d1.Day).AddSeconds(-1);
                    }
                }
                else if (item.TimeType == ContextTimeContextTimeTypeEnum.Year)
                {
                    DateTime d = item.BeginTime.Value.Date;
                    item.BeginTime = new DateTime(d.Year, 1, 1);
                    if (item.EndTime.HasValue && item.EndTime != DateTime.MaxValue)
                    {
                        DateTime d1 = item.EndTime.Value.Date;
                        item.EndTime = new DateTime(d1.Year, 1, 1).AddSeconds(-1);
                    }
                }

                if (lastParam == null)
                {
                    lastParam = item;
                }
                else
                {
                    if (lastParam.BeginTime > item.BeginTime)
                    {
                        if (lastParam.EndTime == DateTime.MaxValue)
                        {
                            throw new CommonException("context context endtime error");
                        }
                        else
                        {
                            item.BeginTime = lastParam.EndTime.Value.AddSeconds(1);
                        }
                    }
                    else if (lastParam.BeginTime < item.BeginTime)
                    {
                        if (lastParam.EndTime == DateTime.MaxValue)
                        {
                            lastParam.EndTime = item.BeginTime.Value.AddSeconds(-1);
                        }
                        else
                        {
                            item.BeginTime = lastParam.EndTime.Value.AddSeconds(1);
                        }
                    }
                    else if (lastParam.BeginTime == item.BeginTime)
                    {
                        if (lastParam.EndTime == DateTime.MaxValue)
                        {
                            throw new CommonException("context context endtime error");
                        }
                        else
                        {
                            item.BeginTime = lastParam.EndTime.Value.AddSeconds(1);
                        }
                    }

                    lastParam = item;
                }
            }

            timeParams = timeParams.OrderBy(t => t.BeginTime).ThenBy(t => t.EndTime).ToList();
            DateTime now = DateTime.Now.AddDays(1);
            foreach (var item in timeParams)
            {
                DateTime b = item.BeginTime.Value;
                while (b < item.EndTime && b <= now)
                {
                    DateTime bt = b;
                    string code = string.Empty;
                    if (item.TimeType == ContextTimeContextTimeTypeEnum.Month)
                    {
                        code = b.ToString("yyyyMM");
                        b = b.AddMonths(item.Time);
                    }
                    else if (item.TimeType == ContextTimeContextTimeTypeEnum.Year)
                    {
                        code = b.ToString("yyyy");
                        b = b.AddYears(item.Time);
                    }
                    else if (item.TimeType == ContextTimeContextTimeTypeEnum.Day)
                    {
                        code = b.ToString("yyMMdd");
                        b = b.AddDays(item.Time);
                    }

                    DateTime et = b.AddSeconds(-1);
                    if (item.EndTime != DateTime.MaxValue)
                    {
                        et = et > item.EndTime.Value ? item.EndTime.Value : et;
                    }

                    if (item.Hosts.Any())
                    {
                        item.Hosts = item.Hosts.OrderBy(t => t.ContextIndex).ToList();
                        int index = 0;
                        foreach (var h in item.Hosts)
                        {
                            var indexCount = h.ContextCount;
                            if (indexCount == 0)
                            {
                                indexCount = 1;
                            }

                            for (int i = 0; i < indexCount; i++)
                            {
                                var contextParam = new CommonContextParam
                                {
                                    Code = index.ToString().PadLeft(2, '0') + code.PadLeft(6, '0'),
                                    BeginTime = bt,
                                    EndTime = et,
                                    ReadBeginTime = bt,
                                    ReadEndTime = et
                                };
                                contextParam.ContextIndex = index;
                                if (item.DbType == DataBaseTypeEnum.PostgreSQL)
                                {
                                    var dataBaseName = moduleCode + index.ToString().PadLeft(2, '0') + code;
                                    contextParam.ConnString = string.Format("User ID=postgres;Password={0};Host={1};Port={2};Database={3};{4}", h.Password, h.Host, h.Post, dataBaseName, h.CustomParam);
                                }

                                index++;
                                contextParam.DBType = item.DbType;
                                contextParam.ContextType = ContextTypeEnum.TimeContext;
                                ret.Add(contextParam);
                            }
                        }
                    }
                }
            }

            return ret;
        }
    }
}
